운영체제·시스템 용어

OS, 프로세스, IPC, 캐시, DB 등 시스템 동작 용어

  • 용어 49개

OS (Operating System, 운영체제)

CPU, 메모리, 디스크 같은 하드웨어 자원을 관리하고 프로그램이 쓸 수 있게 해 주는 기본 소프트웨어.

kernel (커널)

운영체제의 핵심 부분. 하드웨어를 직접 제어하고 프로세스, 메모리, 파일, 네트워크를 관리한다.

  • 메모리는 커널 영역(kernel space)과 사용자 영역(user space)으로 나뉜다. 커널은 커널 영역에서 실행되고, 일반 프로그램은 사용자 영역에서 실행된다.
  • 사용자 영역은 커널의 일부가 아니다. 사용자 프로그램은 system call로 커널에 기능을 요청한다.
  • 학습 노트: 리눅스 커널 학습 로드맵

system call (시스템 콜)

사용자 프로그램이 커널 기능을 요청하는 공식 통로.

process / thread

process는 실행 중인 프로그램(독립된 주소 공간), thread는 프로세스 안의 실행 흐름 단위(주소 공간 공유).

daemon (데몬)

백그라운드에서 계속 실행되며, 터미널 세션과 연결되지 않은 채 요청을 기다리거나 주기적으로 일하는 프로세스.

signal (시그널)

프로세스에 “어떤 일이 생겼다”고 비동기로 알리는 소프트웨어 신호.

IPC (Inter-Process Communication)

프로세스끼리 데이터를 주고받는 방법.

shared memory (공유 메모리)

여러 프로세스가 같은 물리 메모리 영역을 각자의 주소 공간에 연결해서 함께 쓰는 방식.

pipe (파이프)

한 프로세스의 출력을 다른 프로세스의 입력으로 잇는 단방향 바이트 흐름 통로.

  • 쉘의 ls | grep txt가 대표적이다. 이름이 있는 파이프는 named pipe(FIFO).

message queue (메시지 큐)

프로세스 사이에 메시지(덩어리 단위)를 큐에 넣고 꺼내며 주고받는 방식.

semaphore / mutex (세마포어 / 뮤텍스)

동시 접근을 제어하는 도구. mutex는 한 번에 하나만 들어가게 하는 잠금, semaphore는 동시에 N개까지 허용하는 카운터.

concurrency / parallelism (동시성 / 병렬성)

동시성은 여러 작업이 겹쳐서 “함께 진행되는 것처럼” 처리되도록 구조를 짜는 것, 병렬성은 실제로 같은 순간에 여러 작업이 여러 CPU 코어에서 실행되는 것.

  • 철자는 concurrency다. 동시성은 코어가 하나여도 작업을 번갈아 실행하면 성립한다(예: 요리사 한 명이 여러 요리를 번갈아 하는 것). 병렬성은 요리사가 여러 명이어야 한다.
  • 서버에서 동시성이 중요한 이유: 여러 요청이 같은 데이터나 자원을 동시에 건드리기 때문에 순서에 따라 결과가 달라지는 문제(race condition, deadlock)가 생긴다. 이를 막는 것이 동시성 제어다(mutex, semaphore, CAS, DB 락 등).
  • 관련: 멀티스레드, 비동기 처리, 분산 락.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation · 병렬성, 싱글-멀티스레드 서버, 세마포어, 병렬 자료구조

스레드 공유 영역 vs 전용 영역

같은 프로세스 안의 스레드끼리 무엇을 함께 쓰고 무엇을 각자 갖는지의 구분.

  • 공유: heap, 전역·static 변수, 코드 영역, 열린 파일. 여러 스레드가 동시에 접근하므로 동기화가 필요하다(race condition의 원인).
  • 전용: 각 스레드의 stack(지역 변수), CPU 레지스터, 그리고 TLS(thread-local storage). 다른 스레드가 건드리지 않아 동기화가 필요 없다.
  • heap은 프로세스 하나에 하나이고 모든 스레드가 같은 heap을 쓴다(“스레드별 heap”이라는 영역은 따로 없다). 다만 성능을 위해 할당기가 스레드마다 heap의 일부를 미리 떼어 주는 경우가 있다. 예) glibc malloc의 스레드별 arena, Java의 TLAB(Thread-Local Allocation Buffer). 이 조각에서 할당만 스레드별로 하고, 만들어진 객체는 다른 스레드도 볼 수 있다.
  • 관련 노트: IPC와 스레드 메모리 공유 비교
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

TLS (Thread-Local Storage) / ThreadLocal

스레드마다 따로 갖는 전용 저장 공간. 같은 이름의 변수라도 스레드마다 별개의 값을 가진다. “공유” 공간이 아니라 “공유하지 않는” 공간이다.

  • 락 없이도 스레드끼리 값이 섞이지 않아서, 요청마다 따로 들고 다녀야 하는 정보(사용자 정보, trace ID, 트랜잭션)를 파라미터로 넘기지 않고 보관하는 데 쓴다.
  • 예) Java ThreadLocal, C의 __thread / C++의 thread_local, POSIX pthread_key_*. C의 errno도 스레드별로 따로 있다.
  • 주의 1: 스레드 풀은 스레드를 재사용하므로, 요청이 끝나면 remove()로 지우지 않으면 다음 요청에 이전 값이 남는다(오염, 메모리 누수).
  • 주의 2: 비동기 처리로 작업이 다른 스레드로 넘어가면 값이 따라가지 않는다. 그래서 context propagation이 따로 필요하다(MSA·분산 시스템 노트의 분산 추적 참고).
  • 보안 용어의 TLS(Transport Layer Security)와 약어가 같으니 문맥으로 구분한다.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation · 분산 추적(Distributed Tracing)과 비동기 스레드 컨텍스트 전파 아키텍처 (TraceContext, Netty, Spring Batch)

thread pool (스레드 풀)

스레드를 미리 일정 수 만들어 두고, 작업이 올 때마다 하나씩 꺼내 쓰고 끝나면 반납해 재사용하는 구조. “미리 만들어 둔 스레드 묶음 + 작업 대기열(task queue)“이다.

  • 요청마다 스레드를 만들고 없애면 생성·소멸 비용(시간, 스택 메모리)이 들고, 요청이 몰릴 때 스레드가 무한정 늘어 서버가 무너질 수 있다. 풀은 이 둘을 막는다.
  • 동작: 작업 제출 → 대기열에 쌓임 → 놀고 있는 worker 스레드가 꺼내 실행 → 끝나면 다시 대기. 풀 크기가 동시에 실행되는 작업 수의 상한이 된다.
  • 크기 가이드: CPU를 쓰는 작업은 코어 수 근처, 대기(I/O, 외부 호출)가 많은 작업은 더 크게 잡는다. 대기열이 가득 차면 거부 정책(예외 발생, 호출한 스레드가 직접 실행, 버림)을 정한다.
  • Java의 ExecutorService/ThreadPoolExecutor(core·max 크기, 대기열, 거부 정책)가 대표적이다.
  • 주의: 모든 스레드가 느린 호출에 묶이면 thread pool exhaustion, 대기열을 무한으로 두면 메모리 부족(OOM), 풀 안의 작업이 같은 풀의 다른 작업을 기다리면 deadlock, 스레드를 재사용하므로 ThreadLocal은 요청이 끝날 때 지워야 한다.
  • 학습 노트: 01. Netty 아키텍처와 이벤트 루프 스레드 모델 (EventLoop, Boss·Worker, Bootstrap) · 스레드별 전용 큐와 해시 샤딩 — 락 경합 없는 고성능 워커 패턴 · 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

thread-safe (스레드 안전)

여러 스레드가 동시에 접근해도 결과가 올바른 코드나 자료구조.

reentrant (재진입 가능)

함수가 실행되는 도중에 다시 호출되어도(재귀, 다른 스레드, 시그널 핸들러에서의 호출) 올바르게 동작하는 성질.

  • 조건: 전역 변수나 static 변수 같은 공유 상태를 건드리지 않고, 호출자가 넘긴 값과 지역 변수만 쓰고, 재진입 불가능한 함수를 호출하지 않는다.
  • 예) C의 strtok은 내부 static 상태를 써서 재진입 불가이고, strtok_r(reentrant)은 상태를 호출자가 넘긴다. localtime과 localtime_r도 같은 관계다.
  • thread-safe와 비슷하지만 같지 않다. 재진입 가능하면 보통 thread-safe지만, 락으로 thread-safe하게 만든 함수는 같은 스레드가 도중에 다시 호출하면 deadlock이 날 수 있어 재진입 불가일 수 있다.
  • 시그널 핸들러 안에서는 재진입 가능한(async-signal-safe) 함수만 안전하게 쓸 수 있다.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

reentrant lock (재진입 락)

이미 그 락을 쥔 스레드가 같은 락을 다시 얻어도 막히지 않는 락.

  • Java의 synchronized와 ReentrantLock이 해당한다. 락을 쥔 메서드가 같은 락이 필요한 다른 메서드를 호출할 때 스스로 deadlock에 빠지지 않는다. 몇 번 얻었는지 세어 두었다가 같은 횟수만큼 풀어야 완전히 해제된다.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

스레드 동시성 문제 한눈에 보기

여러 스레드가 함께 돌 때 생기는 대표적인 오류들. 이름이 비슷해 헷갈리므로 증상 기준으로 구분한다.

문제증상대표 원인대응
race condition실행 순서에 따라 결과가 달라짐, 가끔만 틀림공유 자원 동시 접근lock, atomic, CAS
data race동기화 없는 동시 읽기/쓰기보호 없이 공유 변수 사용동기화, 불변 객체
deadlock스레드들이 영원히 멈춤서로 상대의 락을 기다림락 순서 통일, timeout
livelockCPU는 쓰는데 진전이 없음서로 양보만 반복무작위 대기(backoff)
starvation특정 스레드만 계속 못 실행됨불공정한 락, 낮은 우선순위공정 락, aging
priority inversion높은 우선순위가 오래 대기낮은 우선순위가 락을 쥐고 있음priority inheritance
visibility / reordering다른 스레드가 바뀐 값을 못 봄CPU 캐시, 컴파일러·CPU 명령 재배치volatile, memory barrier
원자성 위반 (check-then-act)검사와 실행 사이에 상태가 바뀜여러 단계를 한 덩어리로 묶지 않음락으로 묶기, 원자적 연산

race condition (경쟁 상태)

여러 스레드가 같은 자원에 접근하고, 실행 순서(타이밍)에 따라 결과가 달라지는 문제.

  • 예) 두 스레드가 count++를 동시에 실행. count++는 읽기 → 더하기 → 쓰기 3단계라서, 둘 다 5를 읽고 각자 6을 쓰면 7이어야 할 값이 6이 된다(갱신 손실, lost update).
  • 재현이 어렵고 가끔만 터지는 것이 특징이다. 해결: lock으로 구간을 보호하거나 atomic 연산, CAS를 쓴다.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

data race

동기화 없이 여러 스레드가 같은 메모리에 동시에 접근하고, 그중 하나 이상이 쓰기인 상태.

  • race condition의 대표적인 원인이다. C/C++에서는 data race가 있으면 동작이 정의되지 않는다(undefined behavior). 모든 race condition이 data race인 것은 아니고, 락을 써도 검사와 실행 사이에 끼어드는 논리적 race condition은 남을 수 있다.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

deadlock (교착 상태)

둘 이상의 스레드가 서로 상대가 쥔 락을 기다리며 영원히 멈추는 상태.

  • 예) 스레드 1은 락 A를 잡고 B를 기다리고, 스레드 2는 락 B를 잡고 A를 기다린다.
  • 발생 조건 4가지(Coffman): 상호 배제, 점유하며 대기, 비선점(빼앗을 수 없음), 순환 대기. 하나라도 깨면 막을 수 있다.
  • 예방: 모든 스레드가 락을 같은 순서로 잡기, 락 획득에 timeout 두기(tryLock), 락을 잡고 있는 구간을 짧게 하기.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

livelock

스레드가 멈춰 있지는 않지만 서로 양보하며 상태만 바꾸고 일은 진전되지 않는 상태.

starvation (기아)

특정 스레드가 자원(락, CPU)을 계속 얻지 못해 실행되지 못하는 상태.

priority inversion (우선순위 역전)

높은 우선순위 스레드가 낮은 우선순위 스레드가 쥔 락을 기다리는데, 중간 우선순위 스레드가 낮은 쪽을 계속 밀어내서 높은 쪽이 오래 대기하는 현상.

visibility / reordering (가시성 / 명령 재배치)

한 스레드가 바꾼 값을 다른 스레드가 보지 못하거나, 예상과 다른 순서로 보이는 문제.

  • 원인: 값이 CPU 캐시나 레지스터에 머물러 있거나, 컴파일러·CPU가 성능을 위해 명령 순서를 바꾸기 때문.
  • 해결: Java의 volatile, 락, memory barrier로 “먼저 일어난 일이 다른 스레드에 보이도록” 보장한다(happens-before).
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

atomicity violation (check-then-act)

“검사하고 나서 실행”하는 사이에 다른 스레드가 상태를 바꿔서 검사 결과가 무효가 되는 문제.

  • 예) if (!map.containsKey(k)) map.put(k, v); 사이에 다른 스레드가 같은 키를 넣을 수 있다. 검사와 실행을 락으로 묶거나 putIfAbsent 같은 원자적 연산을 쓴다.
  • 보안에서는 TOCTOU(time-of-check to time-of-use)라 부르기도 한다.
  • 학습 노트: 스레드 동시성 문제 정리 - race·deadlock·livelock·starvation

thread pool exhaustion (스레드 풀 고갈)

모든 스레드가 응답을 기다리며 묶여서 새 요청을 처리할 스레드가 없는 상태.

endian (little endian / big endian)

멀티바이트 값(정수 등)을 메모리에 저장할 때 바이트를 어떤 순서로 놓느냐의 규칙(바이트 순서, byte order).

  • little endian은 낮은 주소에 값의 낮은 자리 바이트부터, big endian은 낮은 주소에 값의 높은 자리 바이트부터 저장한다. “큰 주소부터 읽는다”가 아니라 “어느 자리의 바이트를 낮은 주소에 두느냐”가 기준이다.
  • 비트 순서가 아니라 바이트 순서다. 1바이트 값(char, 문자열의 한 글자)은 영향이 없다.
  • x86, 대부분의 ARM 환경은 little endian이다. 네트워크 전송은 big endian으로 약속되어 있다(네트워크 바이트 순서).
  • 다른 endian 시스템끼리 바이너리 데이터를 주고받거나 파일로 읽으면 값이 뒤집혀 보인다. 그래서 전송 전후에 변환해야 한다(C의 htons/htonl/ntohs/ntohl).
  • 학습 노트: 리틀엔디안-빅엔디안 · 엔디안(바이트 순서)과 인터넷 체크섬 — LSB·MSB와 htons를 직접 만들어보기 · 3. 바이트 골격 — 비트플래그·엔디안·Pack·Unpack

0x12345678(4바이트 정수)을 주소 0x100부터 저장할 때:

주소0x1000x1010x1020x103
big endian12345678
little endian78563412
  • big endian: 낮은 주소에 값의 가장 높은 자리 바이트(12)가 온다. 사람이 숫자를 쓰는 순서와 같다.
  • little endian: 낮은 주소에 값의 가장 낮은 자리 바이트(78)가 온다.

stream (스트림)

데이터를 경계 없이 순서대로 연속해서 흘려보내고, 받는 쪽이 필요한 만큼 순차 처리하는 방식.

  • TCP는 바이트 스트림이다. 메시지 경계는 상위 프로토콜이 정한다.
  • HTTP/2의 stream은 하나의 연결 안에서 독립적으로 오가는 요청-응답 흐름 하나를 가리킨다(여러 개가 동시에 가능).

panic (패닉)

복구할 수 없는 치명적인 오류를 만나 프로그램이나 커널이 스스로 멈추는 것.

  • 커널 panic: OS 전체가 중단된다. Go, Rust 등의 panic: 프로그램이 중단된다(처리하지 않으면 종료).
  • 원인은 하드웨어 장애일 수도 있지만 코드 버그, 메모리 오류인 경우도 많다.

segmentation fault / core dump

segmentation fault는 허용되지 않은 메모리에 접근해서 OS가 프로세스를 강제로 종료하는 것, core dump는 그 순간의 메모리 상태를 저장한 파일.

gdb (GNU Debugger)

C/C++ 프로그램을 디버그하는 대표적인 도구. Go, Rust 등도 일부 지원한다.

memory leak (메모리 누수)

사용이 끝난 메모리를 반납(해제)하지 않아, 정리되지 못한 메모리가 계속 쌓이는 상태.

heap vs stack (메모리 영역)

stack은 함수 호출 때 지역 변수가 자동으로 쌓이고 함수가 끝나면 자동으로 사라지는 영역, heap은 실행 중에 필요한 만큼 직접 할당하고 직접(또는 GC가) 해제하는 영역.

  • stack은 빠르고 크기가 작다. 너무 깊게 호출하면 stack overflow가 난다. 자료구조 stack(LIFO)과 같은 방식으로 동작한다(개발 기본 노트 참고).
  • heap은 크기가 크고 유연하지만 해제를 잊으면 메모리 누수가 생긴다.
  • 학습 노트: 메모리 구조 · 메모리 구조 및 const · 동적할당

malloc / free (동적 메모리 할당)

C에서 실행 중에 heap의 메모리를 요청하는 함수(malloc)와 반납하는 함수(free).

OOM (Out Of Memory)

메모리가 부족해서 더 할당할 수 없는 상태 또는 그 오류.

CAS (Compare-And-Swap)

“현재 값이 내가 기대한 값과 같을 때만 새 값으로 바꾼다”를 CPU가 한 번에(원자적으로) 처리하는 명령. 락 없이 동시성을 다루는 기반이다.

cache (캐시)

자주 쓰는 데이터를 더 빠른 곳에 임시로 보관해 두는 것.

local cache (로컬 캐시)

그 프로세스나 서버 안(주로 메모리)에 둔 캐시.

write-through / write-behind (write-back)

캐시에 쓴 데이터를 원본 저장소(DB 등)에 언제 반영하느냐에 따른 쓰기 방식.

  • write-through: 캐시와 DB에 동시에 쓴다(동기). 안전하지만 쓰기가 느리다.
  • write-behind(= write-back): 캐시에만 먼저 쓰고 응답한 뒤, DB에는 나중에 모아서 비동기로 반영한다. 쓰기가 빠르고 DB 부하가 줄지만, 반영 전에 캐시가 죽으면 그 데이터가 유실될 수 있다.
  • “나중에 쓰기”는 맞지만 DB에만 쓰이는 말이 아니다. 캐시, 디스크 버퍼 등 “빠른 쪽에 먼저 쓰고 느린 쪽에 나중에 반영”하는 모든 경우에 쓴다.
  • 학습 노트: 캐시 전략과 운영 이슈 - Redis·stampede·무효화·write-behind

stale (스테일)

원본이 바뀌었는데 갱신되지 않아 현재와 어긋난, 낡은 상태의 데이터·연결·세션·캐시.

stateless / stateful

서버가 요청 사이의 상태를 기억하지 않으면 stateless, 기억하면 stateful.

  • stateless는 서버를 늘리고 장애를 복구하기 쉽다.

DB (Database, 데이터베이스)

데이터를 구조적으로 저장하고 조회·수정·삭제하는 시스템.

transaction (트랜잭션)

여러 작업을 하나처럼 처리하는 단위. 전부 성공하거나 전부 취소된다.

CRUD

Create(생성), Read(조회), Update(수정), Delete(삭제). 데이터를 다루는 기본 4가지 동작.

System V IPC / POSIX IPC

프로세스 간 통신 자원(메시지 큐, 공유메모리, 세마포어)을 제공하는 두 가지 인터페이스 계열.

  • System V: 숫자 key로 찾고, 핸들(msqid)이 파일 디스크립터가 아니다(epoll에 넣을 수 없다). 상태는 ipcs로 본다.
  • POSIX: 이름("/name")으로 찾고, Linux에서는 메시지 큐 디스크립터가 파일 디스크립터라 epoll에 넣을 수 있다.
  • 둘 다 프로세스가 끝나도 자원이 커널에 남는다. 직접 삭제해야 한다.
  • 학습 노트: POSIX IPC와 System V IPC 비교 — 메시지 큐 중심

Unix domain socket (유닉스 도메인 소켓)

같은 장비의 프로세스끼리 통신하는 소켓. 주소가 IP:포트가 아니라 파일 경로다(AF_UNIX).

  • ls -l에서 파일 종류가 s로 보인다.
  • 상대가 종료하면 연결이 닫혀 서버가 즉시 알 수 있다. 그래서 클라이언트 프로세스의 생사 감시에 쓰이기도 한다. 메시지 큐나 공유메모리는 상대가 죽어도 아무 일도 일어나지 않는다.

msgmax / msgmnb / msgmni (메시지 큐 커널 한도)

System V 메시지 큐의 커널 한도. msgmax는 메시지 1건의 최대 크기, msgmnb는 큐 하나에 쌓을 수 있는 총 바이트, msgmni는 시스템 전체 큐 개수.

  • /proc/sys/kernel/ 아래에서 확인한다. msgmni 기본값은 Linux 3.19부터 32,000이고, 넘으면 msgget이 ENOSPC로 실패한다. 큐를 만들면 msg_qbytes가 msgmnb 값으로 설정된다.
  • 소프트웨어가 요청한 메시지 크기가 msgmax보다 크면 줄여서 쓰는 경우가 있다.
  • 학습 노트: POSIX IPC와 System V IPC 비교 — 메시지 큐 중심